const router = require("koa-router")();
const axios = require("axios");
const { muser } = require('../models/muser')
const { bill } = require('../models/bill')
const { product } = require('../models/bill')
const { menu } = require('../models/menu')
const { role } = require('../models/mrole')
const { supplier } = require('../models/supplier')
const { supplierProduct } = require('../models/supplierProduct')
const { dbjson } = require('../dbjson/index.js')

const AIURL = 'http://localhost:11434/api/generate'
const LANGMODEL = 'qwen3:8b'

const cleanAndParseResponse = (response) => {
    try {
        // 1. 去除think标签
        const noThinkTags = response.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
        return noThinkTags.trim()
    } catch (e) {
        console.error("响应解析失败", e);
        return null;
    }
}


router.get("/nai", async (ctx) => {
    const prompt = ctx.query.prompt || '';
    if (!prompt) {
        ctx.status = 400;
        ctx.body = { error: '请提供prompt参数' };
        return;
    }
    const step1 = await simplePrompt(prompt)
    console.log(step1.response, 'step1.response')
    let rcollects = JSON.parse(cleanAndParseResponse(step1.response))
    // console.log(rcollects,'rcollects')
    let lcollects = rcollects
    console.log('提示词分析完毕', JSON.stringify(lcollects))

    const step2 = await generateMongodbQuery(lcollects, prompt)

    let step2res = JSON.parse(cleanAndParseResponse(step2.response))
    console.log('数据库构建完毕', JSON.stringify(step2res))

    // let step3 = await createMongodb(step2res)
    // let step3res = cleanAndParseResponse(step3.response)
    // console.log('数据库最终查询语句构建完毕', JSON.stringify(step3res))

    let step4 = await validateMongoQuery(JSON.stringify(step2res), dbjson, lcollects)
    let step4res = cleanAndParseResponse(step4.response)
    console.log('数据库最终验证结果', JSON.stringify(JSON.parse(step4res).correctedQuery))
    let step5 = await dynamicQuery(JSON.parse(step4res).correctedQuery)
    let step5res = step5
    console.log('查询完成', JSON.stringify(step5res))
    let step6 = await speak(step5, lcollects)
    let step6res = cleanAndParseResponse(step6.response)
    console.log('准备响应结果', JSON.stringify(step6res))
    ctx.body = cleanAndParseResponse(step6.response)
})

const simplePrompt = async (prompt) => {
    return new Promise(async (resolve, reject) => {
        try {
            const response = await axios.post(AIURL, {
                model: LANGMODEL,
                prompt: `你是MongoDB数据模型专家，需根据自然语言问题和数据库模型结构，识别相关数据集合、字段及集合间关系。

数据库模型结构：
${JSON.stringify(dbjson, null, 2)}

用户问题：${prompt}

处理步骤：
1. 分析问题关键词和意图，识别是否需要多集合联合查询
2. 匹配数据库模型中的集合和字段，特别注意关联字段
3. 对匹配结果排序，返回最相关的1-3个集合
4. 明确指出集合间关系及关联字段（如适用）
5. 识别关系类型（一对一/一对多/多对多）及连接方向

输出格式（仅返回JSON，无额外文本）：
{
  "relevantCollections": [
    {
      "collectionName": "集合名称",
      "relevanceScore": 0.0-1.0,
      "relevantFields": ["字段1", "字段2", ...],
      "reasoning": "相关性解释",
      "relationships": [
        {
          "targetCollection": "关联集合名称",
          "localField": "本地关联字段",
          "foreignField": "外部关联字段",
          "relationshipType": "one-to-one|one-to-many|many-to-many",
          "direction": "forward|reverse"
        }
      ]
    }
  ]
}

注意事项：
- 严格基于提供的数据库模型，不编造不存在的集合或字段
- 如涉及多集合查询，必须在relationships数组中详细说明关联方式
- 输出必须是可直接解析的JSON对象，无任何前置或后置文本
- 如无相关集合，返回{"relevantCollections": []}`,
                stream: false
            });
            resolve(response.data)
        } catch (error) {
            reject(error)
        }
    })
}


const generateMongodbQuery = (collects, prompt) => {
    return new Promise(async (resolve, reject) => {
        try {
            const response = await axios.post(AIURL, {
                model: LANGMODEL,
                prompt: `# MongoDB查询生成专家系统提示词

 角色定义
你是一位专业的MongoDB查询生成专家，精通MongoDB查询语法和数据建模最佳实践。你的任务是根据用户的自然语言问题和提供的数据库模型结构，生成严谨、高效、语法正确的MongoDB查询语句。

 数据库模型结构
${dbjson}
可能涉及的数据库集合信息及字段信息
${collects}结构
"relevantCollections": [
    {
      "collectionName": "集合名称",
      "relevanceScore": 0.0-1.0,
      "relevantFields": ["字段1", "字段2", ...],
      "reasoning": "为什么这个集合与问题相关"
    }
  ]
 查询生成规则
1. **集合选择**：严格根据用户问题中的关键词匹配dbjson中的集合名称和描述，仅使用上述模型中已定义最适合的集合可能涉及连表查询
2. **字段验证**：只允许使用指定集合中已定义的字段，禁止使用不存在的字段
3. **数据类型**：必须根据字段描述推断正确的数据类型（数字/字符串/布尔值/数组/对象）
4. **操作符使用**：
   - 数值比较：使用$gt/$lt/$gte/$lte/$eq/$ne
   - 字符串匹配：使用$regex（需添加i选项忽略大小写）或$eq
   - 数组操作：使用$in/$nin/$all/$elemMatch
   - 逻辑组合：使用$and/$or/$not/$nor
5. **关联查询**：如需跨集合查询，必须使用$lookup指定from、localField、foreignField和as
   - 一对多关系必须添加$unwind阶段处理数组结果
   - 多对多关系需考虑使用双重$lookup或$graphLookup
   - 添加$match阶段过滤关联结果，减少数据处理量
   - 对大型数据集使用$lookup的let/pipeline语法进行优化
6. **投影控制**：默认返回所有字段，如需限制返回字段必须显式指定投影（_id字段需显式排除）
7. **排序与限制**：根据问题隐含需求添加sort和limit选项
8. **聚合管道**：复杂统计分析必须使用aggregate管道，包含$match/$group/$project/$sort/$limit等阶段
9. **索引优化**：自动检测并建议对关联字段添加索引
 输出格式要求
必须返回JSON格式的查询对象，包含以下字段：
{
  "collection": "目标集合名称",
  "method": "查询方法(find/findOne/aggregate/count等)",
  "filter": 查询条件对象,
  "projection": 投影对象(可选),
  "sort": 排序对象(可选),
  "limit": 数量限制(可选),
  "skip": 跳过数量(可选),
  "pipeline": 聚合管道数组(当method为aggregate时必填),
  "indexSuggestions": ["建议的索引1", "建议的索引2"]
}

思考过程要求
1. 分析用户问题，提取核心查询意图和关键词
2. 匹配dbjson中的集合和字段，确定相关数据来源
3. 设计查询逻辑，选择合适的查询方法和操作符
4. 验证查询语法和字段合法性
5. 生成符合输出格式的查询对象
6. 提供索引优化建议

 用户问题
${prompt}`,
                stream: false
            });
            resolve(response.data)
        } catch (error) {
            reject(error)
        }
    })
}

const createMongodb = (rules) => {
    return new Promise(async (resolve, reject) => {
        try {
            const response = await axios.post(AIURL, {
                model: LANGMODEL,
                prompt: `你是一个MongoDB查询转换专家，需要将提供的JSON查询结构转换为实际可执行的MongoDB查询语句。

JSON查询结构：
${rules}

转换要求：
1. 严格按照MongoDB官方语法进行转换
2. 只需要查询语句
3. 保留所有查询条件、聚合管道和投影设置
4. 使用JavaScript语法格式，适合在Node.js环境中执行
5. 确保集合名称、方法和参数正确映射
6. 不需要添加额外注释或解释

输出格式：
直接返回可执行的MongoDB查询代码，不需要任何额外文本`,
                stream: false
            });
            resolve(response.data)
        } catch (error) {
            reject(error)
        }
    })
}

const validateMongoQuery = async (generatedQuery, dbSchema, userPrompt) => {
    return new Promise(async (resolve, reject) => {
        try {
            const response = await axios.post(AIURL, {
                model: LANGMODEL,
                prompt: `你是MongoDB查询验证与优化专家，负责检查和修正生成的数据库查询。

数据库模式定义：
${JSON.stringify(dbSchema, null, 2)}

用户原始问题：${userPrompt}

生成的查询：
${JSON.stringify(generatedQuery, null, 2)}

任务要求：
1. 严格验证查询与数据库模式的一致性
2. 检查以下可能的错误类型：
   - 集合名称是否存在于数据库模式中
   - 使用的字段是否与集合定义匹配
   - 数据类型是否与字段定义兼容
   - 查询操作符是否适用于字段类型
   - 聚合管道阶段是否按逻辑顺序排列
   - 是否存在性能优化空间
3. 关联查询专项检查：
   - $lookup阶段是否必要且高效
   - 是否正确使用索引字段作为关联条件
   - 大型数据集是否使用了适当的分页和限制
   - 是否可以通过投影减少传输数据量
   - 是否可以通过$match阶段前置过滤减少数据处理量

验证步骤：
1. 分析生成的查询结构和意图
2. 逐项与数据库模式进行比对
3. 标记所有不一致之处并解释原因
4. 如果发现错误，提供修正后的查询
5. 如果查询正确，确认其有效性
6. 提供性能优化建议

输出格式：
{
  "isValid": true/false,
  "errors": ["错误描述1", "错误描述2"...],
  "correctedQuery": {修正后的查询对象转化为{
  "collection": "目标集合名称",
  "method": "查询方法(find/findOne/aggregate/count等)",
  "filter": 查询条件对象,
  "projection": 投影对象(可选),
  "sort": 排序对象(可选),
  "limit": 数量限制(可选),
  "skip": 跳过数量(可选),
  "pipeline": 聚合管道数组(当method为aggregate时必填)
},
  "performanceSuggestions": [
    "索引优化建议",
    "管道阶段优化建议",
    "数据过滤建议"
  ],
  "explanation": "验证结果的详细解释"
}`,
                stream: false
            });
            resolve(response.data);
        } catch (error) {
            reject(error);
        }
    });
};


const validateResultQuery = async (generatedQuery, dbSchema, userPrompt) => {
    return new Promise(async (resolve, reject) => {
        try {
            const response = await axios.post(AIURL, {
                model: LANGMODEL,
                prompt: `你是一位资深数据统计专家，负责全面检查和精确修正统计结果。你的工作包括：

1. 统计方法验证
- 评估统计方法与业务目标的匹配度
- 验证样本选择的代表性和无偏性
- 检查统计假设条件是否满足

2. 数据质量审核
- 识别异常值、缺失值和不一致数据
- 验证数据转换和预处理步骤
- 确认数据来源的可靠性和时效性

3. 计算精确性检查
- 重新验证关键统计指标的计算公式
- 核对汇总数据与明细数据的一致性
- 验证统计显著性和置信区间计算

4. 结果解释合理性
- 检查统计结果与业务逻辑的一致性
- 验证趋势分析和预测的合理性
- 识别潜在的统计误导或过度解读

5. 修正与优化
- 提供明确的错误定位和修正建议
- 推荐更合适的统计方法或可视化方式
- 优化统计流程以提高准确性和效率

请基于以上职责，对提供的统计结果进行全面检查，并以以下JSON格式返回结果：
{
  "validity": true/false,
  "errors": [
    {
      "location": "具体位置",
      "type": "错误类型",
      "description": "错误描述",
      "correction": "修正建议"
    }
  ],
  "correctedResults": {},
  "methodImprovements": ["改进建议1", "改进建议2"],
  "confidenceScore": 0.0-1.0
}`,
                stream: false
            });
            resolve(response.data);
        } catch (error) {
            reject(error);
        }
    });
};

// 映射集合名称到模型
const modelMap = {
    muser,
    bill,
    product,
    menu,
    role,
    supplier,
    supplierProduct
};

/**
 * 动态查询函数
 * @param {Object} queryConfig 查询配置对象
 * @returns {Promise<*>} 查询结果
 */
async function dynamicQuery(queryConfig) {
    const {
        collection,
        method,
        filter = {},
        projection,
        sort,
        limit,
        skip,
        pipeline,
        updateData
    } = queryConfig;

    const Model = modelMap[collection];

    if (!Model) {
        throw new Error(`未找到集合: ${collection}`);
    }

    let dbQuery = Model;

    // 根据 method 判断执行哪种操作
    switch (method.toLowerCase()) {
        case 'find':
            dbQuery = dbQuery.find(filter, projection);
            if (sort) dbQuery = dbQuery.sort(sort);
            if (limit) dbQuery = dbQuery.limit(limit);
            if (skip) dbQuery = dbQuery.skip(skip);
            return await dbQuery.exec();

        case 'findone':
            return await dbQuery.findOne(filter, projection).exec();

        case 'aggregate':
            if (!pipeline || !Array.isArray(pipeline)) {
                throw new Error('aggregate 方法必须提供 pipeline 数组');
            }
            return await dbQuery.aggregate(pipeline).exec();

        case 'count':
            return await dbQuery.countDocuments(filter);

        case 'updateone':
            if (!updateData) throw new Error('updateone 方法必须提供 updateData');
            return await dbQuery.updateOne(filter, updateData);

        case 'update':
        case 'updatemany':
            if (!updateData) throw new Error('updateMany 方法必须提供 updateData');
            return await dbQuery.updateMany(filter, updateData);

        case 'deleteone':
            return await dbQuery.deleteOne(filter);

        case 'deletemany':
            return await dbQuery.deleteMany(filter);

        case 'create':
            return await dbQuery.create(filter);

        default:
            throw new Error(`不支持的方法: ${method}`);
    }
}


async function speak(result, prompt) {
    return new Promise(async (resolve, reject) => {
        try {
            const response = await axios.post(AIURL, {
                model: LANGMODEL,
                prompt: `你是一个MongoDB数据结果分析专家，需要根据用户的自然语言问题和数据库查询结果：

${JSON.stringify(result, null, 2)}

用户问题：${JSON.stringify(prompt, null, 2)}


处理步骤：
1. 分析用户问题中的关键词和意图
2. 讲结果与用户问题进行回应如果没关系则直接返回结果
3. 要求语言信达雅
- 解释推理过程时要简明扼要`,
                stream: false
            });
            resolve(response.data)
        } catch (error) {
            reject(error)
        }
    })
}

module.exports = router;